﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace Inaction.Util
{
    public class TypeHelper
    {
        public static List<T> GetSubClassInstance<T>() where T : class
        {
            List<T> list = new List<T>();
            List<Assembly> assemblies = new List<Assembly>();
            var files = System.IO.Directory.GetFiles(AppHelper.ExecuteDirectory, "*.dll");
            foreach (var file in files)
            {
                try
                {
                    assemblies.Add(Assembly.LoadFrom(file));
                }
                catch { }
            }
            assemblies.Add(Assembly.GetExecutingAssembly());

            foreach (var assembly in assemblies)
            {
                try
                {
                    var types = assembly.GetTypes();
                    foreach (var type in types)
                    {
                        if (type.IsAbstract) continue;
                        if (typeof(T).IsInterface && type.GetInterfaces().Contains(typeof(T))//T为接口
                       || type.IsSubclassOf(typeof(T)) && !type.IsAbstract)//T为类
                        {
                            var obj = (T)assembly.CreateInstance(type.FullName);
                            if (obj != null)
                            {
                                list.Add(obj);
                            }
                        }
                    }
                }
                catch
                { continue; }
            }
            return list;
        }
        public static List<Type> GetSubClassType<T>(string file) where T : class
        {
            List<Type> list = new List<Type>();
            var assembly = Assembly.LoadFrom(file);
            var types = assembly.GetTypes();
            foreach (var type in types)
            {
                if (type.IsAbstract) continue;
                if (typeof(T).IsInterface && type.GetInterfaces().Contains(typeof(T))
                    || type.IsSubclassOf(typeof(T)))
                {

                    list.Add(type);
                }
            }
            return list;
        }
        public static List<Type> GetSubClassType<T>() where T : class
        {
            var list = GetSubClassTypeFromDLL<T>();
            return list;
        }
        public static List<Type> GetSubClassTypeFromDLL<T>() where T : class
        {
            List<Type> list = new List<Type>();
            List<Assembly> assemblies = new List<Assembly>();
            assemblies.AddRange(GetAssemblyFromDLL());
            return GetTypeList<T>(assemblies);
        }
        public static List<Type> GetSubClassTypeFromDLL<T>(string file) where T : class
        {
            List<Type> list = new List<Type>();
            List<Assembly> assemblies = new List<Assembly>();
            assemblies.Add(Assembly.LoadFrom(file));
            return GetTypeList<T>(assemblies);
        }
        private static List<Type> GetTypeList<T>(List<Assembly> assemblies) where T : class
        {
            List<Type> list = new List<Type>();
            foreach (var assembly in assemblies)
            {
                try
                {
                    var types = assembly.GetTypes();
                    foreach (Type type in types)
                    {
                        if (type.IsAbstract) continue;
                        if (typeof(T).IsInterface && type.GetInterfaces().Contains(typeof(T))//T为接口
                            || type.IsSubclassOf(typeof(T)))//T为类
                        {
                            if (!list.Contains(type))
                            {
                                list.Add(type);
                            }
                        }
                    }
                }
                catch (Exception) { continue; }
            }
            return list;
        }
        private static List<Assembly> GetAssemblyFromDLL()
        {
            List<Assembly> assemblies = new List<Assembly>();
            var files = System.IO.Directory.GetFiles(AppHelper.ExecuteDirectory, "*.dll");
            foreach (var file in files)
            {
                assemblies.Add(Assembly.LoadFrom(file));
            }
            return assemblies;
        }
        public static object CreateInstance(string assemblyName, string fullName)
        {
            if (!assemblyName.EndsWith(".dll", StringComparison.OrdinalIgnoreCase)
                && !assemblyName.EndsWith(".exe", StringComparison.OrdinalIgnoreCase))
            {
                if (System.IO.File.Exists(assemblyName + ".dll"))
                {
                    assemblyName = assemblyName + ".dll";
                }
                else if (System.IO.File.Exists(assemblyName + ".exe"))
                {
                    assemblyName = assemblyName + ".exe";
                }
            }
            return Assembly.LoadFrom(assemblyName.Trim()).CreateInstance(fullName.Trim());
        }
        public static T CreateInstance<T>() where T : class
        {
            Assembly assembly = typeof(T).Assembly;
            return (T)assembly.CreateInstance(typeof(T).FullName);
        }
        public static List<T> GetSubClassInstance<T>(string filePath) where T : class
        {
            List<T> list = new List<T>();
            var assembly = Assembly.LoadFrom(filePath);
            var types = assembly.GetTypes();
            foreach (var type in types)
            {
                if (type.IsAbstract) continue;
                if (typeof(T).IsInterface && type.GetInterfaces().Contains(typeof(T))
                    || type.IsSubclassOf(typeof(T)))
                {
                    var obj = (T)assembly.CreateInstance(type.FullName);
                    if (obj != null)
                    {
                        list.Add(obj);
                    }
                }
            }
            return list;
        }
        public static T LoadTypeInstance<T>(string assemblyName, string classFullName, params object[] args) where T : class
        {
            if (string.IsNullOrEmpty(assemblyName) && string.IsNullOrEmpty(classFullName))
            {
                return null;
            }
            if (!assemblyName.EndsWith(".dll", StringComparison.OrdinalIgnoreCase) && !assemblyName.EndsWith(".exe", StringComparison.OrdinalIgnoreCase))
            {
                if (System.IO.File.Exists(assemblyName + ".dll"))
                {
                    assemblyName += ".dll";
                }
                else
                {
                    assemblyName += ".exe";
                }
            }
            assemblyName = Inaction.Util.AppHelper.ExecuteDirectory + assemblyName;
            var assembly = System.Reflection.Assembly.LoadFrom(assemblyName);
            return (T)assembly.CreateInstance(classFullName, true, BindingFlags.Default, null, args, System.Globalization.CultureInfo.CurrentUICulture, null);
        }

        #region Method
        public static MethodInfo GetStaticMethodInfo(Type entityType, string methodName, Type[] argsType, System.Reflection.BindingFlags flags = System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public)
        {
            var type = entityType;
            if (!entityMethods.ContainsKey(entityType))
            {
                entityMethods.Add(entityType, new List<MethodInfo>());
            }
            var methods = entityMethods[entityType];
            if (methods.Count > 0)
            {
                foreach (var method in methods)
                {
                    var paras = method.GetParameters();
                    bool matched = true;
                    foreach (var para in paras)
                    {
                        if (!argsType.Contains(para.ParameterType))
                        {
                            matched = false;
                            break;
                        }
                    }
                    if (matched)
                    {
                        return method;
                    }
                }
            }
            MethodInfo retMethod = null;
            while (!type.Name.Equals("Object"))
            {
                retMethod = type.GetMethod(methodName, flags, null, argsType, null);
                if (retMethod == null) type = type.BaseType;
                else break;
            }
            if (retMethod != null)
            {
                methods.Add(retMethod);
            }
            return retMethod;
        }

        private static Dictionary<Type, List<MethodInfo>> entityMethods = new Dictionary<Type, List<MethodInfo>>();
        #endregion
    }
}
